/*
   Copyright 2011 marcopar@gmail.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package eu.flatworld.worldexplorer.gui;

import eu.flatworld.commons.Storage;
import eu.flatworld.commons.log.LogX;
import eu.flatworld.worldexplorer.Config;
import eu.flatworld.worldexplorer.WorldExplorer;
import eu.flatworld.worldexplorer.boundaries.BoundariesList;
import eu.flatworld.worldexplorer.gui.options.OptionsDialog;
import eu.flatworld.worldexplorer.layer.Layer;
import eu.flatworld.worldexplorer.layer.bmng.BMNGLayer;
import eu.flatworld.worldexplorer.layer.nltl7.NLTL7Layer;
import eu.flatworld.worldexplorer.placenames.PlacenamesList;
import eu.flatworld.worldexplorer.provider.AbstractProvider;
import eu.flatworld.worldexplorer.provider.DefaultProviderChain;
import eu.flatworld.worldexplorer.provider.MemoryProvider;
import eu.flatworld.worldexplorer.provider.Provider;
import eu.flatworld.worldexplorer.swing.MapSurface;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import javax.swing.SwingUtilities;

public class MainFrame extends javax.swing.JFrame implements PropertyChangeListener {

    private DefaultProviderChain providerChain;
    private BMNGLayer bmngLayer;
    private NLTL7Layer nltl7Layer;
    private OptionsDialog optionsDialog = null;
    private BoundariesList boundaries = null;
    private PlacenamesList placenames = null;
    private boolean shuttingDown = false;

    public MainFrame() throws IOException {
        Runtime.getRuntime().addShutdownHook(new ShutdownThread());
        Splash.setMessage("Initializing interface...");
        initComponents();
        startup();
        Splash.setMessage("Showtime...");
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mapSurface = new eu.flatworld.worldexplorer.swing.MapSurface();
        statusBar = new eu.flatworld.worldexplorer.gui.StatusBar();
        jMenuBar1 = new javax.swing.JMenuBar();
        jmFile = new javax.swing.JMenu();
        jmiFileOptions = new javax.swing.JMenuItem();
        jSeparator1 = new javax.swing.JSeparator();
        jmiFileExit = new javax.swing.JMenuItem();
        jmView = new javax.swing.JMenu();
        jmiViewViewgrid = new javax.swing.JCheckBoxMenuItem();
        jmiViewViewcenter = new javax.swing.JCheckBoxMenuItem();
        jSeparator2 = new javax.swing.JSeparator();
        jmiViewViewBoundaries = new javax.swing.JCheckBoxMenuItem();
        jmiViewViewPlacenames = new javax.swing.JCheckBoxMenuItem();
        jmHelp = new javax.swing.JMenu();
        jmiHelpHelpContents = new javax.swing.JMenuItem();
        jmiHelpAbout = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setTitle(WorldExplorer.NAME + " " + WorldExplorer.VERSION);
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                formWindowClosing(evt);
            }
        });

        javax.swing.GroupLayout mapSurfaceLayout = new javax.swing.GroupLayout(mapSurface);
        mapSurface.setLayout(mapSurfaceLayout);
        mapSurfaceLayout.setHorizontalGroup(
            mapSurfaceLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 790, Short.MAX_VALUE)
        );
        mapSurfaceLayout.setVerticalGroup(
            mapSurfaceLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 532, Short.MAX_VALUE)
        );

        getContentPane().add(mapSurface, java.awt.BorderLayout.CENTER);

        statusBar.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        getContentPane().add(statusBar, java.awt.BorderLayout.SOUTH);

        jmFile.setMnemonic('f');
        jmFile.setText("File");

        jmiFileOptions.setMnemonic('p');
        jmiFileOptions.setText("Options");
        jmiFileOptions.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jmiFileOptionsActionPerformed(evt);
            }
        });
        jmFile.add(jmiFileOptions);
        jmFile.add(jSeparator1);

        jmiFileExit.setMnemonic('x');
        jmiFileExit.setText("Exit");
        jmiFileExit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jmiFileExitActionPerformed(evt);
            }
        });
        jmFile.add(jmiFileExit);

        jMenuBar1.add(jmFile);

        jmView.setMnemonic('v');
        jmView.setText("View");

        jmiViewViewgrid.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F5, 0));
        jmiViewViewgrid.setMnemonic('g');
        jmiViewViewgrid.setText("View grid");
        jmiViewViewgrid.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jmiViewViewgridActionPerformed(evt);
            }
        });
        jmView.add(jmiViewViewgrid);

        jmiViewViewcenter.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F6, 0));
        jmiViewViewcenter.setMnemonic('c');
        jmiViewViewcenter.setText("View center");
        jmiViewViewcenter.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jmiViewViewcenterActionPerformed(evt);
            }
        });
        jmView.add(jmiViewViewcenter);
        jmView.add(jSeparator2);

        jmiViewViewBoundaries.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F7, 0));
        jmiViewViewBoundaries.setMnemonic('b');
        jmiViewViewBoundaries.setText("View boundaries");
        jmiViewViewBoundaries.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jmiViewViewBoundariesActionPerformed(evt);
            }
        });
        jmView.add(jmiViewViewBoundaries);

        jmiViewViewPlacenames.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F8, 0));
        jmiViewViewPlacenames.setMnemonic('p');
        jmiViewViewPlacenames.setText("View placenames");
        jmiViewViewPlacenames.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jmiViewViewPlacenamesActionPerformed(evt);
            }
        });
        jmView.add(jmiViewViewPlacenames);

        jMenuBar1.add(jmView);

        jmHelp.setMnemonic('h');
        jmHelp.setText("Help");

        jmiHelpHelpContents.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F1, 0));
        jmiHelpHelpContents.setMnemonic('c');
        jmiHelpHelpContents.setText("Help Contents");
        jmiHelpHelpContents.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jmiHelpHelpContentsActionPerformed(evt);
            }
        });
        jmHelp.add(jmiHelpHelpContents);

        jmiHelpAbout.setMnemonic('a');
        jmiHelpAbout.setText("About");
        jmiHelpAbout.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jmiHelpAboutActionPerformed(evt);
            }
        });
        jmHelp.add(jmiHelpAbout);

        jMenuBar1.add(jmHelp);

        setJMenuBar(jMenuBar1);

        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((screenSize.width-800)/2, (screenSize.height-600)/2, 800, 600);
    }// </editor-fold>//GEN-END:initComponents
    private void jmiHelpAboutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jmiHelpAboutActionPerformed
        new AboutDialog(this, false).setVisible(true);
    }//GEN-LAST:event_jmiHelpAboutActionPerformed

    private void jmiFileOptionsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jmiFileOptionsActionPerformed
        synchronized (this) {
            if (optionsDialog == null) {
                optionsDialog = new OptionsDialog();
            }
            if (optionsDialog.isVisible()) {
                optionsDialog.toFront();
            } else {
                optionsDialog.showDialog();
            }
        }
    }//GEN-LAST:event_jmiFileOptionsActionPerformed

    private void jmiViewViewcenterActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jmiViewViewcenterActionPerformed
        boolean value = jmiViewViewcenter.isSelected();
        mapSurface.setShowCenter(value);
        Config.getConfig().setShowCenter(value);
    }//GEN-LAST:event_jmiViewViewcenterActionPerformed

    private void jmiViewViewgridActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jmiViewViewgridActionPerformed
        boolean value = jmiViewViewgrid.isSelected();
        mapSurface.setShowGrid(value);
        Config.getConfig().setShowGrid(value);
    }//GEN-LAST:event_jmiViewViewgridActionPerformed

    private void jmiFileExitActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jmiFileExitActionPerformed
        shutdown();
    }//GEN-LAST:event_jmiFileExitActionPerformed

    private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing
        shutdown();
    }//GEN-LAST:event_formWindowClosing

    private void jmiViewViewBoundariesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jmiViewViewBoundariesActionPerformed
        boolean value = jmiViewViewBoundaries.isSelected();
        mapSurface.setShowBoundaries(value);
        Config.getConfig().setShowBoundaries(value);
}//GEN-LAST:event_jmiViewViewBoundariesActionPerformed

    private void jmiViewViewPlacenamesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jmiViewViewPlacenamesActionPerformed
        boolean value = jmiViewViewPlacenames.isSelected();
        mapSurface.setShowPlacenames(value);
        Config.getConfig().setShowPlacenames(value);
    }//GEN-LAST:event_jmiViewViewPlacenamesActionPerformed

    private void jmiHelpHelpContentsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jmiHelpHelpContentsActionPerformed
        new HelpDialog(this, false).setVisible(true);
    }//GEN-LAST:event_jmiHelpHelpContentsActionPerformed

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JMenu jmFile;
    private javax.swing.JMenu jmHelp;
    private javax.swing.JMenu jmView;
    private javax.swing.JMenuItem jmiFileExit;
    private javax.swing.JMenuItem jmiFileOptions;
    private javax.swing.JMenuItem jmiHelpAbout;
    private javax.swing.JMenuItem jmiHelpHelpContents;
    private javax.swing.JCheckBoxMenuItem jmiViewViewBoundaries;
    private javax.swing.JCheckBoxMenuItem jmiViewViewPlacenames;
    private javax.swing.JCheckBoxMenuItem jmiViewViewcenter;
    private javax.swing.JCheckBoxMenuItem jmiViewViewgrid;
    private eu.flatworld.worldexplorer.swing.MapSurface mapSurface;
    private eu.flatworld.worldexplorer.gui.StatusBar statusBar;
    // End of variables declaration//GEN-END:variables
    void startup() throws IOException {
        Splash.setMessage("Beginning startup sequence...");
        Storage.getStorage().set(MainFrame.class.getName(), this);

        Splash.setMessage("Loading preferences...");
        loadPreferences();
        Level l = Level.parse(Config.getConfig().getLogLevel());
        LogX.log(Level.INFO, "Setting log level to: " + l);
        LogX.setLevel(l);

        Splash.setMessage("Updating interface...");
        updateInterface();
        mapSurface.setShowCenter(Config.getConfig().isShowCenter());
        mapSurface.setShowGrid(Config.getConfig().isShowGrid());
        mapSurface.setShowBoundaries(Config.getConfig().isShowBoundaries());
        mapSurface.setShowPlacenames(Config.getConfig().isShowPlacenames());

        Splash.setMessage("Initializing layers and providers...");
        bmngLayer = new BMNGLayer();
        nltl7Layer = new NLTL7Layer();

        reinitLayers();
        try {
            boundaries = new BoundariesList(Config.getConfig().getDataPath() + "/Earth/Boundaries/World/pathlist.idx", Config.getConfig().getDataPath() + "/Earth/Boundaries/World/pathlist.pkg");
        } catch (IOException ex) {
            LogX.log(Level.WARNING, "Error loading boundaries", ex);
        }
        mapSurface.setBoundaries(boundaries);
        placenames = new PlacenamesList();
        try {
            placenames.readAll(Config.getConfig().getDataPath() + "/Earth/Placenames");
        } catch (IOException ex) {
            LogX.log(Level.WARNING, "Error loading placenames", ex);
        }
        mapSurface.setPlacenames(placenames);
        MemoryProvider memoryProvider = new MemoryProvider(Config.getConfig().getMinimumMemoryLimit());
        providerChain = new DefaultProviderChain();
        providerChain.setMemoryProvider(memoryProvider);
        switchToLayer(bmngLayer);
        mapSurface.addPropertyChangeListener(this);
        mapSurface.setCoordinadetes(new Point2D.Double(10.9167, 44.6667));
        updateStatusBar();

        Splash.setMessage("Startup completed...");
    }

    void shutdown() {
        if (!shuttingDown) {
            shuttingDown = true;
            LogX.log(Level.INFO, "Shutting down...");
            storePreferences();
            dispose();
            System.exit(0);
        }
    }

    void updateInterface() {
        jmiViewViewcenter.setSelected(Config.getConfig().isShowCenter());
        jmiViewViewgrid.setSelected(Config.getConfig().isShowGrid());
        jmiViewViewBoundaries.setSelected(Config.getConfig().isShowBoundaries());
        jmiViewViewPlacenames.setSelected(Config.getConfig().isShowPlacenames());
    }

    void loadPreferences() {
        File f = new File(WorldExplorer.OPTIONS_FILE);
        if (!f.exists()) {
            try {
                Config.getConfig().store();
            } catch (Exception ex) {
                LogX.log(Level.SEVERE, "Error creating preferences file: " + f.getAbsolutePath(), ex);
            }
        }

        try {
            Config.getConfig().load();
        } catch (IOException ex) {
            LogX.log(Level.SEVERE, "Error loading preferences file: " + f.getAbsolutePath(), ex);
        }

        LogX.log(Level.INFO, "Preferences loaded: " + f.getAbsolutePath());
    }

    void storePreferences() {
        File f = new File(WorldExplorer.OPTIONS_FILE);
        try {
            Config.getConfig().store();
        } catch (Exception ex) {
            LogX.log(Level.SEVERE, "Error storing preferences file: " + f.getAbsolutePath(), ex);
        }
    }

    void updateStatusBar() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                String s = String.format("%s layer %d - %2.2fX - (%3.4f/%3.4f)", mapSurface.getLayer().getName(),
                        mapSurface.getLayerNumber(), mapSurface.getZoom(), mapSurface.getCoordinates().getX(),
                        mapSurface.getCoordinates().getY());
                if (mapSurface.getMeasureLine() != null) {
                    s = String.format("%s - (%3.4f/%3.4f)->(%3.4f/%3.4f) length=%.4f km", s,
                            mapSurface.getMeasureLine().getP1().getLongitude(), mapSurface.getMeasureLine().getP1().getLatitude(),
                            mapSurface.getMeasureLine().getP2().getLongitude(), mapSurface.getMeasureLine().getP2().getLatitude(),
                            mapSurface.getMeasureLine().getLength() / 1000);
                    statusBar.setStatusText(s);
                }
                statusBar.setStatusText(s);
            }
        });
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getSource() == mapSurface) {
            if (evt.getPropertyName().equals(MapSurface.P_SURFACE_ZOOM)) {
                if (mapSurface.getAperture().getX() < 2.25) {
                    if (mapSurface.getLayer() instanceof BMNGLayer) {
                        switchToLayer(nltl7Layer);
                    }
                }
                if (mapSurface.getAperture().getX() > 4.50) {
                    if (mapSurface.getLayer() instanceof NLTL7Layer) {
                        switchToLayer(bmngLayer);
                    }
                }
                updateStatusBar();
            }
            if (evt.getPropertyName().equals(MapSurface.P_SURFACE_MOVE)) {
                updateStatusBar();
            }
            if (evt.getPropertyName().equals(MapSurface.P_SURFACE_MEASURE)) {
                updateStatusBar();
            }
        }
        if (evt.getSource() instanceof AbstractProvider) {
            if (evt.getPropertyName().equals(AbstractProvider.P_IDLE)) {
                if ((Boolean) evt.getNewValue() == false) {
                    statusBar.setStatusProgressBarVisible(true);
                    statusBar.getStatusProgressBar().setIndeterminate(true);
                } else {
                    ArrayList<Provider> lp = providerChain.getProviders();
                    int qs = 0;
                    for (Provider provider : lp) {
                        qs += ((AbstractProvider) provider).getQueueSize();
                    }
                    if (qs == 0) {
                        statusBar.setStatusProgressBarVisible(false);
                        statusBar.getStatusProgressBar().setIndeterminate(false);
                    }
                }
            }
        }
    }

    void switchToLayer(Layer layer) {
        double zoom = 1;
        if (layer instanceof BMNGLayer) {
            nltl7Layer.getCacheProvider().removePropertyChangeListener(this);
            nltl7Layer.getDataProvider().removePropertyChangeListener(this);
            nltl7Layer.getRemoteProvider().removePropertyChangeListener(this);
            bmngLayer.getCacheProvider().addPropertyChangeListener(this);
            bmngLayer.getDataProvider().addPropertyChangeListener(this);
            bmngLayer.getRemoteProvider().addPropertyChangeListener(this);
            providerChain.setCacheProvider(bmngLayer.getCacheProvider());
            providerChain.setDataProvider(bmngLayer.getDataProvider());
            providerChain.setRemoteProvider(bmngLayer.getRemoteProvider());
            zoom = 1;
        }
        if (layer instanceof NLTL7Layer) {
            bmngLayer.getCacheProvider().removePropertyChangeListener(this);
            bmngLayer.getDataProvider().removePropertyChangeListener(this);
            bmngLayer.getRemoteProvider().removePropertyChangeListener(this);
            nltl7Layer.getCacheProvider().addPropertyChangeListener(this);
            nltl7Layer.getDataProvider().addPropertyChangeListener(this);
            nltl7Layer.getRemoteProvider().addPropertyChangeListener(this);
            providerChain.setCacheProvider(nltl7Layer.getCacheProvider());
            providerChain.setDataProvider(nltl7Layer.getDataProvider());
            providerChain.setRemoteProvider(nltl7Layer.getRemoteProvider());
            zoom = 8;
        }
        mapSurface.switchToLayer(layer, providerChain, zoom);
        mapSurface.repaint();
    }

    public void reinitLayers() {
        bmngLayer.reinitProviders();
        nltl7Layer.reinitProviders();
        if (mapSurface.getLayer() instanceof BMNGLayer) {
            providerChain.setCacheProvider(bmngLayer.getCacheProvider());
            providerChain.setDataProvider(bmngLayer.getDataProvider());
            providerChain.setRemoteProvider(bmngLayer.getRemoteProvider());
        }
        if (mapSurface.getLayer() instanceof NLTL7Layer) {
            providerChain.setCacheProvider(nltl7Layer.getCacheProvider());
            providerChain.setDataProvider(nltl7Layer.getDataProvider());
            providerChain.setRemoteProvider(nltl7Layer.getRemoteProvider());
        }
    }

    class ShutdownThread extends Thread {

        public ShutdownThread() {

        }

        @Override
        public void run() {
            shutdown();
        }
    }
}
